package com.heima.lock;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class StockController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private WmLockMapper lockMapper;

    @Autowired
    private RedissonClient redissonClient;

    @GetMapping("/stock")
    public String stock() {
        synchronized ("abc") {
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            } else {
                System.out.println("库存不足！！！");
            }
        }
        return "OK";
    }

    /**
     * 分布式锁：基于mysql实现
     *
     * @return
     */
    @GetMapping("/stock1")
    public String stock1() {
        //1. 获取分布式锁：谁能往表中成功的插入数据，那么就表示谁抢到了锁
        try {
            //添加锁：向数据库中位子字段新增数据，添加成功执行代码，添加失败：抛出异常
            //添加的数值：一般为当亲的线程 ID  Thread.currentThread().getId()
            WmLock lock = WmLock.builder().mylock(String.valueOf(Thread.currentThread().getId())).build();
            lockMapper.insert(lock);

            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            } else {
                System.out.println("库存不足！！！");
            }

            //释放锁：将数据删除
            lockMapper.deleteById(lock.getId());
        } catch (Exception e) {
            System.out.println("没有抢到分布式锁");
        }

        return "OK";
    }


    /**
     * 分布式锁：基于redis实现
     *
     * @return
     */
    @GetMapping("/stock2")
    public String stock2() {
        //1. 获取分布式锁：谁能往redis中成功的插入数据，那么就表示谁抢到了锁
        //参数： 1 ： key名称  2：key对应的value 通常使用当前线程Id 3,4： 设置锁的过期时间，单位秒
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent("lock",
                String.valueOf(Thread.currentThread().getId()),
                30, TimeUnit.SECONDS);

        if (isLock) {
            //使用 try  catch  finally 防止业务超时 或者 业务错误的情况 导致锁无法释放
            try {
                //获取到锁：
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {

                    try {
                        //模拟业务耗时
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    stock--;

                    //模拟业务的错误
                    int i = 1 / 0;

                    redisTemplate.opsForValue().set("stock", stock + "");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                } else {
                    System.out.println("库存不足！！！");
                }

                //释放锁
                //redisTemplate.delete("lock");

            } catch (NumberFormatException e) {
                e.printStackTrace();
                System.out.println("业务运行超时！");
            } finally {
                //释放锁： 注意： 这时需要堆 key 和 value 都进行比对，防止释放其他 线程的锁
                //获取锁的  value
                String value = redisTemplate.opsForValue().get("lock");
                //比对： 当前的锁，是否是自己的锁 ：是删除锁
                if (value != null && value.equals(String.valueOf(Thread.currentThread().getId()))) {
                    //释放锁
                    redisTemplate.delete("lock");
                }
            }

        } else {
            System.out.println("没有抢到分布式锁");
        }
        return "OK";
    }


    /**
     * 分布式锁：基于 Redisson
     * 1：导入坐标 ，编写 配置类
     * 2: 注入 RedissonClient
     * @return
     */
    @GetMapping("/stock3")
    public String stock3() {
        //1：获取： 锁的 操作对象   还未加锁   设置锁的 key 名称
        RLock lock = redissonClient.getLock("lock");

        try {
            //加锁: 推荐使用默认值： 默认过期时间 30S 锁的value 为当前线程的 线程ID
            // 默认时底层会使用  开门狗技术
            //看门狗技术： 锁的续期： 业务执行 每经过 1/3 （10S）时，会自动续期到  30S
            lock.lock();//阻塞锁：  获取不到锁，会一直尝试，直到获取到锁
            //lock.tryLock();//非阻塞锁：  给时间的值  就为阻塞所
            //阻塞所：不阻塞所：  阻塞所，获取不到会一直尝试获取，直到获取为止
            //非阻塞锁：  获取不到就立刻返回 走 finilly  trylock 给时间： 在这时间内为阻塞：会一致尝试获取
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            } else {
                System.out.println("库存不足！！！");
            }


        } catch (NumberFormatException e) {
            e.printStackTrace();
            System.out.println("未强盗锁！");
        } finally {
            //释放锁
            lock.unlock();
        }


        return "OK";
    }
}